/// 与操作有关的方法（方法有点多，拆分一下）
/// 这一部分都是包括root节点在内的
part of "../tree_node.dart";

extension NodeOperation<T> on TreeNode<T> {
  /// 寻找满足条件的单个子节点
  /// 不包含当前自己这个节点（这是为了如果有root节点，防止把自己算进去）
  T? find(Test<T> test) {
    // 如果自己符合条件，则返回自己
    if (this is T && test(this as T)) return this as T;

    // 对于所有child
    for (final child in children) {
      // 如果满足测试，则返回child
      if (test(child)) return child;

      // 如果没有找到，但child是树节点
      // 则递归查找，如果找到了就返回
      if (child is TreeNode<T>) {
        final result = (child as TreeNode<T>).find(test);
        if (result != null) return result;
      }
    }
    return null;
  }

  T fetch(Test<T> test) {
    final result = find(test);
    // 如果没有找到，则报错
    if (result == null) throw Bug("未根据条件找到TreeNode中的节点");
    return result;
  }

  /// 寻找单个满足条件的子节点，注入新的节点
  /// 当不传入test时，即代表在当前节点插入
  void insert(Test<T> parentTest, T newNode) {
    // 找到单个满足条件的子节点
    T? node = find(parentTest);

    if (node == null || node is! TreeNode<T>) {
      // 如果没找到或者节点不是树节点，则报错
      throw Exception("节点未找到，或者并非是树节点");
    } else {
      // 如果找到，则加入节点
      node.children.add(newNode);
    }
  }

  /// 替换满足条件的子节点
  /// 返回的bool值代表是否替换成功
  bool replace(Test<T> test, T newNode) {
    // 对于所有child
    for (final child in children) {
      // 如果child满足条件，则将child删除，并添加新的node
      if (test(child)) {
        children.remove(child);
        children.add(newNode);
        return true;
      }

      // 如果前者没有替换成功，并且child为树节点，则递归替换
      if (child is TreeNode<T>) {
        return (child as TreeNode<T>).replace(test, newNode);
      }
    }
    return false;
  }

  /// 删除单个子节点，如果匹配多于1个子节点，则报错
  void delete(Test<T> test) {
    // 如果该条件匹配到多个值，则报错
    List<T> matches = findAll(test);
    if (matches.length > 1) throw Exception("找到重复的节点");

    // 否则，执行一次删除所有的操作
    deleteAll(test);
  }

  /// 递归查找所有满足条件的节点（包含自己）
  /// 请注意：查找到的节点如有包含关系，则子节点可能会重复出现
  /// 此方法的结果仅做挑选和判断使用，尽量不要用它的结果进行下一步运算，否则会有重复的危险
  List<T> findAll(Test<T> test) {
    // 建立一个空List
    List<T> result = [];

    // 如果当前节点满足，则加入它
    // 由于Node<T>实际上算是T，所以直接投射type
    final current = this as T;
    if (test(current)) result.add(current);

    // 对于所有子节点，递归加入符合相同条件的所有节点
    for (T child in children) {
      if (child is TreeNode<T>) {
        // 如果child同是树节点，则执行递归操作，并将结果累加
        final found = (child as TreeNode<T>).findAll(test);
        result.addAll(found);
      } else {
        // 否则只添加child节点
        if (test(child)) result.add(child);
      }
    }

    // 返回最终的List
    return result;
  }

  /// 删除所有满足条件的子节点（操作完后会直接修改当前节点的数据，而不会返回任何结果）
  void deleteAll(Test<T> test) {
    // 建立一个新的List（已经执行删除过的children）
    List<T> remain = [];

    // 对于所有children
    for (T child in children) {
      // 判断是否满足删除条件，如无需删除，则加到remain中
      if (!test(child)) remain.add(child);
      // 如果child为树节点，则递归删除子节点
      if (child is TreeNode<T>) (child as TreeNode<T>).deleteAll(test);
    }

    // 清空当前的children，并用删除后的新children替代
    children.clear();
    children.addAll(remain);
  }
}
